Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New lint [local_assigned_single_value] #10977

Closed

Conversation

Centri3
Copy link
Member

@Centri3 Centri3 commented Jun 17, 2023

This one was pretty tough. I still don't know much about MIR so I likely overcomplicated some of it but I hope it's at least of decent quality :)

I don't know of any FPs (that I haven't fixed already), but there's tons of FNs, most notably let (x, y) = (1, 1) and the like. It's pretty barebones currently,

Closes #4016

changelog: New lint [local_assigned_single_value]

@rustbot
Copy link
Collaborator

rustbot commented Jun 17, 2023

r? @giraffate

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jun 17, 2023
@Centri3
Copy link
Member Author

Centri3 commented Jun 17, 2023

r? @Jarcho (apparently you know MIR?)

@rustbot rustbot assigned Jarcho and unassigned giraffate Jun 17, 2023
@Centri3 Centri3 force-pushed the local_assignment_single_value branch from 9a054f6 to 9877a96 Compare June 17, 2023 16:49
@Centri3 Centri3 marked this pull request as draft June 17, 2023 21:03
@Centri3 Centri3 force-pushed the local_assignment_single_value branch from 9877a96 to 3413ae9 Compare June 18, 2023 00:29
@Centri3 Centri3 marked this pull request as ready for review June 18, 2023 21:28
@Centri3
Copy link
Member Author

Centri3 commented Jun 18, 2023

The implementation is pretty much done now, I may refactor again though. I still see no FPs as it's overly pessimistic, if it can't prove with 100% confidence it's constant it won't lint.

There are still missed cases, though, like casting a C-like enum or getting the field of a constant ADT

@Centri3 Centri3 force-pushed the local_assignment_single_value branch from 96cbee6 to 4853c84 Compare June 18, 2023 21:31
@bors
Copy link
Collaborator

bors commented Jun 19, 2023

☔ The latest upstream changes (presumably #10951) made this pull request unmergeable. Please resolve the merge conflicts.

@Centri3 Centri3 force-pushed the local_assignment_single_value branch from 4853c84 to f15e601 Compare June 19, 2023 03:38
@bors
Copy link
Collaborator

bors commented Jun 27, 2023

☔ The latest upstream changes (presumably #10884) made this pull request unmergeable. Please resolve the merge conflicts.

@Centri3 Centri3 force-pushed the local_assignment_single_value branch from f15e601 to d406752 Compare June 27, 2023 21:11
@Centri3 Centri3 force-pushed the local_assignment_single_value branch from d406752 to 5643945 Compare June 27, 2023 21:16
Copy link
Contributor

@Jarcho Jarcho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just did an initial review. Didn't check the evaluation logic yet, but there's a bit of work to do with the current comments.


/// Holds the data we have for the usage of a local.
#[derive(Debug, Default)]
struct LocalUsageValues<'tcx> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better represented as an enum. Something like:

enum State {
    Uninitialized,
    SingleValue(Value, Vec<Span>),
    MultiValue,
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

let mut v = V {
body: mir,
cx,
map: mir.local_decls.iter().map(|_| LocalUsageValues::default()).collect(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map should be initialized to a state such that only user bound mutable variables check the assigned value. i.e. matches!(l.local_info, LocalInfo::User(_)) && l.mutability == Mutability::Mut)

Copy link
Member Author

@Centri3 Centri3 Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, local_info is actually ClearCrossCrate now so we can't. It's not available past borrowck and friends and results in an ICE if used in clippy.

We could perhaps use var_debug_info or something, or LocalKind::Temp.

return;
};

if stmt.source_info.span.from_expansion() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't just ignore assignments from macros. The most permissive thing you can do here is to block linting for any locals which are assigned in macros. Trying to lint macros will be pain if you want to avoid false positives.

span: stmt.source_info.span,
};

if let Rvalue::Use(operand) = rvalue {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole chain should just be a match.

// Handle `let [x, y] = [1, 1]` and `let (x, y) = (1, 1)`
&& matches!(base_proj, PlaceElem::Field(..) | PlaceElem::Index(..))
{
spanned.node = LocalUsage::DependsOnWithProj(place.local, base_proj);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only ok if the source projection is not mutable.

} else {
// It seems sometimes a local's just moved, no projections, so let's make sure we
// don't set `assigned_non_const` to true for these cases
spanned.node = LocalUsage::DependsOn(place.local);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only ok if the source projection is not mutable.

&& matches!(base_proj, PlaceElem::Field(..) | PlaceElem::Index(..))
{
// Probably unnecessary
spanned.node = LocalUsage::DependsOnWithProj(place.local, base_proj);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only ok if the source projection is not mutable.

spanned.node = LocalUsage::DependsOnWithProj(place.local, base_proj);
} else {
// Handle casts from enum discriminants
spanned.node = LocalUsage::DependsOn(place.local);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only ok if the source projection is not mutable.

@Centri3
Copy link
Member Author

Centri3 commented Jul 5, 2023

Thanks for reviewing! I agree that there's a ton of work to do on this. I'll probably rewrite what you didn't review yet as it's even uglier. Unfortunately I might not be able to get to this as I've been having issues with both my physical and mental health, but I'll get to it in due time ^^

@Centri3
Copy link
Member Author

Centri3 commented Aug 8, 2023

I'm closing this for now as I have no intention to come back to it. If anybody else wants to work on this in the next few months, you're free to.

Some things I feel like should be note to anybody working on this in the future: MIR might not be the best for this. Currently, let x = A::B as i32 where A is repr(i32) and has implicit discriminators is lowered to something like let _ = 1i32 as i32, which means we have no way of ignoring this as it shouldn't be linted. This can be fixed upstream, however, there's no real way to determine if an assignment comes before another. So something like

let mut x = 1;
let mut y = 1;
x = y;
y = x;

Will either get into a loop or not lint, despite we should lint it. If this operates on the HIR, we can ignore y = x when evaluating x = y, but: HIR will be tricky. let (mut x, mut y) = (1, 2) will need to be evaluated properly, same with

let (&&&x, [y, z], A { x: _, w, .. }) = (&&&&&&1, [1, 2], A { x: 0, y: 0, z: 0, w: 0 }

and anything else. It'll be a very complex lint no matter what.

@Centri3 Centri3 closed this Aug 8, 2023
@Centri3 Centri3 deleted the local_assignment_single_value branch August 8, 2023 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lint variable only ever assigned a single value
5 participants